﻿using System;
using System.Collections.Generic;
using System.Linq;


namespace FlurentFiltration.Visitors
{
    internal class BaseFilterVisitor
    {
        protected FilterBinding FilterBinding { get; private set; }

        public BaseFilterVisitor(FilterBinding filterBindings)
        {
            FilterBinding = filterBindings;
        }

        protected virtual IEnumerable<T> VisitCollection<T>(IEnumerable<T> oldCollection, IFilter filter,
                                                            Type collectionType)
        {
            if (filter != null && oldCollection != null)
            {
                var bindingExists = FilterBinding.Contains(collectionType);

                if (!bindingExists)
                    throw ExceptionHelper.CreateBindingNotDefinedException(collectionType);

                oldCollection = filter.Filtrate(oldCollection as IEnumerable<object>) as IEnumerable<T>;

                var enumerableProps =
                    typeof(T).GetProperties().Where(x => x.PropertyType.GUID == collectionType.GUID).ToList();


                if (enumerableProps.Any() && oldCollection != null)
                {
                    foreach (var item in oldCollection)
                    {
                        if (item != null)
                        {
                            foreach (var collection in enumerableProps)
                            {
                                var childBindingExsists = FilterBinding.Contains(collection.PropertyType);

                                if (!childBindingExsists)
                                    throw ExceptionHelper.CreateBindingNotDefinedException(typeof (IEnumerable<T>));

                                var childFilterType = FilterBinding.GetValue(collection.PropertyType);
                                var childFilterProp =
                                    filter.GetType()
                                          .GetProperties()
                                          .FirstOrDefault(x => x.PropertyType == childFilterType);

                                if (childFilterProp != null)
                                {

                                    var childFilter = childFilterProp.GetValue(filter, null) as IFilter;

                                    if (childFilter != null)
                                    {
                                        var oldSet = collection.GetValue(item, null) as IEnumerable<object>;
                                        var newSet = VisitCollection(oldSet, childFilter, collection.PropertyType);

                                        if (!collection.PropertyType.IsInstanceOfType(newSet))
                                            throw ExceptionHelper.CreateUnsupportedColTypeException(newSet.GetType(),
                                                                                                    collection
                                                                                                        .PropertyType);

                                        collection.SetValue(item, newSet, null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return oldCollection;
        }
    }
}
